package contract

import (
	"context"
	"crypto/ecdsa"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethclient"
	"go-chain-api/contract/evmAbi"
	"log"
	"math/big"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type Response struct {
	Code int `json:"code"`
	Data struct {
		Type                 string  `json:"type"`
		ChainID              string  `json:"chainId"`
		Nonce                string  `json:"nonce"`
		To                   string  `json:"to"`
		Gas                  string  `json:"gas"`
		GasPrice             string  `json:"gasPrice"`
		MaxPriorityFeePerGas *string `json:"maxPriorityFeePerGas"`
		MaxFeePerGas         *string `json:"maxFeePerGas"`
		Value                string  `json:"value"`
		Input                string  `json:"input"`
		V                    string  `json:"v"`
		R                    string  `json:"r"`
		S                    string  `json:"s"`
		Hash                 string  `json:"hash"`
	} `json:"data"`
	Msg string `json:"msg"`
}

type TRXResponse struct {
	Code int    `json:"code"`
	Data string `json:"data"`
	Msg  string `json:"msg"`
}

// CallContractMethod 通用函数，调用以太坊合约的只读方法
func CallContractMethod(client *ethclient.Client, contractAddress common.Address, contractABI string, methodName string, params ...interface{}) ([]interface{}, error) {
	// 解析合约ABI
	parsedABI, err := abi.JSON(strings.NewReader(contractABI))
	if err != nil {
		return nil, fmt.Errorf("failed to parse contract ABI: %v", err)
	}

	// 打包合约调用数据
	packedData, err := parsedABI.Pack(methodName, params...)
	if err != nil {
		return nil, fmt.Errorf("failed to pack data for method %s: %v", methodName, err)
	}

	callMsg := ethereum.CallMsg{
		To:   &contractAddress,
		Data: packedData,
	}

	// 调用合约
	ctx := context.Background()
	result, err := client.CallContract(ctx, callMsg, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to call contract method %s: %v", methodName, err)
	}

	// 解包合约调用结果
	outputs, err := parsedABI.Unpack(methodName, result)
	if err != nil {
		return nil, fmt.Errorf("failed to unpack return data for method %s: %v", methodName, err)
	}

	return outputs, nil
}

func SendTransaction(client *ethclient.Client, privateKey *ecdsa.PrivateKey, contractAddress common.Address, contractABI string, methodName string, params ...interface{}) (*types.Transaction, error) {
	// 使用带有超时的上下文是一个好习惯
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	// 获取当前链的ID
	ChainID, err := client.ChainID(ctx)
	if err != nil {
		return nil, err
	}

	// 获取发送者地址
	fromAddress := crypto.PubkeyToAddress(privateKey.PublicKey)

	// 获取账户的nonce值
	nonce, err := client.PendingNonceAt(ctx, fromAddress)
	if err != nil {
		return nil, fmt.Errorf("获取nonce失败: %v", err)
	}
	// 获取当前推荐的gas价格
	gasPrice, err := client.SuggestGasPrice(ctx)
	if err != nil {
		return nil, fmt.Errorf("获取gas价格失败: %v", err)
	}

	// 解析合约ABI
	parsedABI, err := abi.JSON(strings.NewReader(contractABI))
	if err != nil {
		return nil, fmt.Errorf("解析合约ABI失败: %v", err)
	}
	for _, param := range params {
		log.Printf("%v", param)
	}
	// 打包要调用的合约方法和参数
	input, err := parsedABI.Pack(methodName, params...)
	if err != nil {
		return nil, fmt.Errorf("打包输入数据失败: %v", err)
	}
	//time.Sleep(15 * time.Second)
	// 估算调用方法所需的gas限制
	gasLimit, err := client.EstimateGas(ctx, ethereum.CallMsg{
		From: fromAddress, // 设置调用者地址
		To:   &contractAddress,
		Data: input,
	})
	if err != nil {
		//去授权
		if err.Error() == "execution reverted: TransferHelper::transferFrom: transferFrom failed" {
			//去授权
			arr := params[2].([]common.Address)[0]
			fmt.Println(arr)
			SendApprove(client, privateKey, contractAddress, evmAbi.ERC20_ABI, params[2].([]common.Address)[0], params[0].(*big.Int))
		}
		//gasLimit = uint64(14400000)
		//fmt.Errorf("估算gas失败: %v", err.Error())
		return nil, fmt.Errorf("估算gas失败: %v", err.Error())
	}

	// 创建交易
	tx := types.NewTransaction(nonce, contractAddress, big.NewInt(0), gasLimit, gasPrice, input)

	// 对交易进行签名，注意这里使用了EIP155签名者，并且需要正确的chainID
	signedTx, err := types.SignTx(tx, types.NewEIP155Signer(ChainID), privateKey)
	if err != nil {
		return nil, fmt.Errorf("签名交易失败: %v", err)
	}

	// 发送交易
	err = client.SendTransaction(ctx, signedTx)
	if err != nil {
		return nil, fmt.Errorf("发送交易失败: %v", err)
	}

	return signedTx, nil
}

func SendApprove(client *ethclient.Client, privateKey *ecdsa.PrivateKey, contractAddress common.Address, contractABI string, SellAddress common.Address, value *big.Int) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()
	// 代币合约地址
	tokenAddress := SellAddress
	// 解析代币合约ABI
	abiByte, err := abi.JSON(strings.NewReader(contractABI))
	if err != nil {
		log.Fatalf("Failed to parse contract ABI: %v", err)
	}

	// 构建交易数据
	spender := contractAddress
	//value := big.NewInt(1000000000000) // 授权的代币数量

	approveData, err := abiByte.Pack("approve", spender, value.Mul(value, big.NewInt(1000)))
	if err != nil {
		log.Fatalf("Failed to pack approve data: %v", err)
	}

	// 构建交易参数
	nonce, err := client.PendingNonceAt(context.Background(), crypto.PubkeyToAddress(privateKey.PublicKey))
	if err != nil {
		log.Fatalf("Failed to retrieve nonce: %v", err)
	}

	gasPrice, err := client.SuggestGasPrice(context.Background())
	if err != nil {
		log.Fatalf("Failed to retrieve gas price: %v", err)
	}

	//gasLimit := uint64(300000) // 自定义燃气限制
	gasLimit, err := client.EstimateGas(ctx, ethereum.CallMsg{
		From: crypto.PubkeyToAddress(privateKey.PublicKey), // 设置调用者地址
		To:   &tokenAddress,
		Data: approveData,
	})
	// 创建交易
	tx := types.NewTransaction(nonce, tokenAddress, big.NewInt(0), gasLimit, gasPrice, approveData)
	ChainID, err := client.ChainID(ctx)
	if err != nil {
		return
	}
	// 签名交易
	signedTx, err := types.SignTx(tx, types.NewEIP155Signer(ChainID), privateKey)
	if err != nil {
		log.Fatalf("Failed to sign transaction: %v", err)
	}

	// 发送交易
	err = client.SendTransaction(context.Background(), signedTx)
	if err != nil {
		log.Fatalf("Failed to send transaction: %v", err)
	}

	log.Printf("Approval transaction sent: %s\n", signedTx.Hash().Hex())
}

func SendTransactionMain(client *ethclient.Client, privateKey *ecdsa.PrivateKey, value *big.Int, toAddress common.Address) (*types.Transaction, error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	// 构建交易参数
	nonce, err := client.PendingNonceAt(context.Background(), crypto.PubkeyToAddress(privateKey.PublicKey))
	if err != nil {
		log.Printf("Failed to retrieve nonce: %v", err)
		return nil, err
	}

	gasPrice, err := client.SuggestGasPrice(context.Background())
	if err != nil {
		log.Printf("Failed to retrieve gas price: %v", err)
		return nil, err
	}
	var Data []byte
	//gasLimit := uint64(300000) // 自定义燃气限制
	gasLimit, err := client.EstimateGas(ctx, ethereum.CallMsg{
		From: crypto.PubkeyToAddress(privateKey.PublicKey), // 设置调用者地址
		To:   &toAddress,
		Data: Data,
	})
	// 创建交易
	tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, Data)
	ChainID, err := client.ChainID(ctx)
	if err != nil {
		log.Printf("Failed to ChainID: %v", err)
		return nil, err
	}
	// 签名交易
	signedTx, err := types.SignTx(tx, types.NewEIP155Signer(ChainID), privateKey)
	if err != nil {
		log.Printf("Failed to sign transaction: %v", err)
		return nil, err
	}

	// 发送交易
	err = client.SendTransaction(context.Background(), signedTx)
	if err != nil {
		log.Printf("Failed to send transaction: %v", err)
		return nil, err
	}
	return signedTx, nil
}

func GetTransactionSign(client *ethclient.Client, businessId string, to string, rpcUrl string, value string, tokenAddress string) (*types.Transaction, error) {
	rand.Seed(int64(uint64(time.Now().UnixNano()))) // 初始化随机数种子
	randomInt := rand.Intn(10000000)                // 生成一个0到99的随机整数
	randomString := strconv.Itoa(randomInt)
	timestamp := strconv.FormatInt(time.Now().Unix(), 10)

	salt := "Zhe9eyanwUd1"
	data := []byte(businessId + to + rpcUrl + value + tokenAddress + randomString + timestamp + salt)
	hash := md5.Sum(data)
	md5String := hex.EncodeToString(hash[:])
	// 调用 b 服务获取签名交易
	//blockwatch专用
	url := "http://192.168.0.188:8888/api/getSign?id=" + businessId + "&to=" + to + "&rpcUrl=" + rpcUrl + "&value=" + value + "&tokenAddress=" + tokenAddress + "&nonce=" + randomString + "&timestamp=" + timestamp + "&encryptData=" + md5String
	fmt.Println("url:", url)
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("调用 b 服务失败:", err)
		return nil, err
	}
	defer resp.Body.Close()

	var response Response
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		fmt.Println("解析响应失败:", err)
		return nil, err
	}

	if response.Code != 200 {
		fmt.Printf("错误代码: %d, 消息: %s\n", response.Code, response.Msg)
		return nil, errors.New("获取签名失败")
	}

	// 解析签名交易数据
	var tx types.Transaction
	txData := map[string]interface{}{
		"nonce":    response.Data.Nonce,
		"gasPrice": response.Data.GasPrice,
		"gas":      response.Data.Gas,
		"to":       response.Data.To,
		"value":    response.Data.Value,
		"input":    response.Data.Input,
		"v":        response.Data.V,
		"r":        response.Data.R,
		"s":        response.Data.S,
	}

	txBytes, err := json.Marshal(txData)
	if err != nil {
		fmt.Println("序列化交易数据失败:", err)
		return nil, err
	}

	if err := tx.UnmarshalJSON(txBytes); err != nil {
		fmt.Println("解析签名交易失败:", err)
		return nil, err
	}

	// 发送签名的交易
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	err = client.SendTransaction(ctx, &tx)
	if err != nil {
		fmt.Println("发送交易失败:", err)
		return nil, errors.New("交易失败")
	}

	//判断交易是否成功
	for {
		receipt, err := client.TransactionReceipt(ctx, tx.Hash())
		if err != nil {
			// 交易还未被挖矿，继续等待
			time.Sleep(1 * time.Second)
			continue
		}

		// 检查交易状态
		if receipt.Status == 0 {
			return &tx, errors.New("交易失败：交易hash:" + tx.Hash().Hex() + "，状态码:" + strconv.Itoa(int(receipt.Status)))
		}

		return &tx, nil
	}

}

func GetTrxTransactionSign(to string, value string, tokenAddress string, businessId string, rpcUrl string) (string, error) {
	rand.Seed(int64(uint64(time.Now().UnixNano()))) // 初始化随机数种子
	randomInt := rand.Intn(10000000)                // 生成一个0到99的随机整数
	randomString := strconv.Itoa(randomInt)
	timestamp := strconv.FormatInt(time.Now().Unix(), 10)

	salt := "Zhe9eyanwUd1"
	data := []byte(businessId + to + rpcUrl + value + tokenAddress + randomString + timestamp + salt)
	hash := md5.Sum(data)
	md5String := hex.EncodeToString(hash[:])

	// 调用 b 服务获取签名交易
	url := "http://192.168.0.188:8888/api/getSign?id=" + businessId + "&to=" + to + "&rpcUrl=" + rpcUrl + "&value=" + value + "&tokenAddress=" + tokenAddress + "&nonce=" + randomString + "&timestamp=" + timestamp + "&encryptData=" + md5String
	fmt.Println("url:", url)
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("调用 b 服务失败:", err)
		return "", err
	}
	defer resp.Body.Close()

	var response TRXResponse
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		fmt.Println("解析响应失败:", err)
		return "", err
	}

	if response.Code != 200 {
		fmt.Printf("错误代码: %d, 消息: %s\n", response.Code, response.Msg)
		return "", errors.New("获取签名失败")
	}

	return response.Data, nil
}
